package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.health.constant.TradeConstants;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.mapper.OrdersCancelledMapper;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.OrdersRefundMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.properties.TradeProperties;
import com.jzo2o.health.service.IMemberService;
import com.jzo2o.health.service.IOrdersService;
import com.jzo2o.health.service.IReservationSettingService;
import com.jzo2o.health.service.ISetmealService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author WangXinyi
 * @since 2024-12-14
 */
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {


    @Autowired
    private ISetmealService setmealService;

    @Autowired
    private IMemberService memberService;

    @Autowired
    private IOrdersService owner;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private IReservationSettingService reservationSettingService;

    /**
     * 下单接口
     *
     * @param dto
     * @return
     */
    @Override
    public PlaceOrderResDTO place(PlaceOrderReqDTO dto) {
        //获取套餐id,通过套餐id查询套餐信息
        Integer setmealId = dto.getSetmealId();
        Setmeal setmeal = setmealService.findById(setmealId);
        if (setmeal == null) {
            throw new BadRequestException("非法参数，套餐不存在");
        }
        //属性拷贝
        Orders orders = BeanUtil.toBean(dto, Orders.class);
        //封装属性
        orders.setId(this.createOrderId());//订单id 19位 前6位为年月日 后面为自增数字
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());//订单状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());//支付状态

        orders.setSetmealName(setmeal.getName());//套餐名字
        orders.setSetmealSex(setmeal.getSex());//套餐使用性别
        orders.setSetmealAge(setmeal.getAge());//套餐使用年龄
        orders.setSetmealImg(setmeal.getImg());//套餐图片
        orders.setSetmealRemark(setmeal.getRemark());//套餐说明
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));//套餐价格

        orders.setMemberId(UserThreadLocal.currentUserId());//用户id
        Member member = memberService.getById(UserThreadLocal.currentUserId());//获取用户对象
        orders.setMemberPhone(member.getPhone());//获取用户电话

        orders.setSortBy(DateUtils.toEpochMilli(LocalDateTime.now()));//排序字段取创建时间的毫秒值
        //创建订单
        owner.add(orders);
        //预约人数+1
        reservationSettingService.updateReservations(orders.getReservationDate());
        //将订单存放到延迟队列中，15分钟到期自动取消订单
        Message messageObject = MessageBuilder.withBody(orders.getId().toString().getBytes(StandardCharsets.UTF_8))
                .setHeader("x-delay", "60000")
                .build();
        rabbitTemplate.convertAndSend(TradeConstants.MQ_DELAY_EXCHANGE, TradeConstants.MQ_DELAY_KEY, messageObject, new CorrelationData());

        return new PlaceOrderResDTO(orders.getId());
    }

    /**
     * 将上述事务代码优化
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(Orders orders) {
        boolean result = this.save(orders);
        if (!result) {
            throw new DBException("下单失败");
        }
    }

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String ORDER_ID_NUM_KEY = "ORDER:ID:NUM";

    @Override
    public Long createOrderId() {
        //获取存入redis自增数据
        Long beginId = redisTemplate.opsForValue().increment(ORDER_ID_NUM_KEY);
        //利用redis的自增策略来自增编号
        Long id = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + beginId;
        return id;
    }


    /**
     * 订单支付
     *
     * @param tradingChannel
     * @param orderId
     * @return
     */
    @Autowired
    private NativePayApi nativePayApi;
    @Autowired
    private TradeProperties tradeProperties;


    /**
     * 生成支付二维码
     *
     * @param tradingChannel
     * @param orderId
     * @return
     */
    @Override
    public OrdersPayResDTO pay(PayChannelEnum tradingChannel, Long orderId) {
        //1.判断订单是否存在
        Orders orders = this.getById(orderId);
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }
        //2.判断订单是否已经处理过
        if (ObjectUtils.equals(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS) || ObjectUtils.isNotNull(orders.getTransactionId())) {
            OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(orders, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(orderId);
            return ordersPayResDTO;
        }
        //3.调用第三方支付接口
        //3.1封装请求参数
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        //获取商户号
        Long enterpriseId = ObjectUtils.equals(tradingChannel, PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId() : tradeProperties.getAliEnterpriseId();
        //判断是否更换过支付渠道
        boolean changeChannel = false;
        if (orders.getTradingChannel() != null && !ObjectUtils.equals(orders.getTradingChannel(), tradingChannel.getValue())) {
            changeChannel = true;//更换过渠道
        }

        nativePayReqDTO.setEnterpriseId(enterpriseId);//商户号
        nativePayReqDTO.setProductAppId(TradeConstants.PRODUCT_APP_ID);//系统业务标识
        nativePayReqDTO.setProductOrderNo(orderId);//业务系统订单号
        nativePayReqDTO.setTradingChannel(tradingChannel);//支付渠道
        // nativePayReqDTO.setTradingAmount(orders.getSetmealPrice());//交易金额
        nativePayReqDTO.setTradingAmount(BigDecimal.valueOf(0.01));//0.01方便测试
        nativePayReqDTO.setChangeChannel(changeChannel);//是否切换过渠道
        nativePayReqDTO.setMemo("体检");//备注信息为订单setMealName

        //调用生成二维码接口
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayReqDTO);
        if (ObjectUtils.isNull(downLineTrading)) {
            throw new BadRequestException("调用支付接口失败");
        }
        //支付成功，更新订单表数据
        owner.ofPay(orderId, downLineTrading);

        //封装响应参数
        OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(downLineTrading, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(orders.getPayStatus());
        return ordersPayResDTO;
    }


    /**
     * 封装生成二维码接口优化
     *
     * @param orderId
     * @param downLineTrading
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void ofPay(Long orderId, NativePayResDTO downLineTrading) {
        boolean update = lambdaUpdate().eq(Orders::getId, orderId)
                .set(Orders::getTradingChannel, downLineTrading.getTradingChannel())  //设置支付渠道
                .set(Orders::getTradingOrderNo, downLineTrading.getTradingOrderNo())//支付服务交易单号
                .update();
        if (!update) {
            throw new DBException("更新订单失败");
        }
    }

    /**
     * 查询支付结果
     *
     * @param orderId
     * @return
     */
    @Autowired
    private TradingApi tradingApi;

    @Override
    public OrdersPayResDTO payResult(Long orderId) {
        //1.判断订单是否存在
        Orders orders = this.getById(orderId);
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }
        //2.判断是否有支付服务交易单号
        if (ObjectUtils.isNull(orders.getTradingOrderNo())) {
            throw new BadRequestException("非法订单");
        }
        //3.判断是否交易成功
        OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(orderId);
        if (ObjectUtils.equals(ordersPayResDTO.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            return ordersPayResDTO;
        }
        //4.调用查询支付信息接口
        TradingResDTO tradResultByTradingOrderNo = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
        if (tradResultByTradingOrderNo == null) {
            throw new ForbiddenOperationException("调用查询支付信息接口失败");
        }
        //5.更新订单表数据pay_time transaction_id pay_status order_status
        owner.ofPayResult(orderId, tradResultByTradingOrderNo.getTransactionId());
        //6.封装响应对象
        ordersPayResDTO.setQrCode(tradResultByTradingOrderNo.getQrCode());//支付二维码图片
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());//支付成功
        return ordersPayResDTO;
    }

    /**
     * 优化事务
     *
     * @param orderId
     * @param transactionId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void ofPayResult(Long orderId,String transactionId) {
        boolean update = lambdaUpdate().eq(Orders::getId, orderId)
                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus()) //支付状态
                .set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus()) //订单状态
                .set(Orders::getPayTime, LocalDateTime.now())//支付时间
                .set(Orders::getTransactionId, transactionId)//第三方交易单号
                .update();
        if (!update) {
            throw new DBException("更新订单表失败");
        }
    }

    @Autowired
    private OrdersCancelledMapper ordersCancelledMapper;

    /**
     * 取消未支付订单
     */
    // private final List<Integer> goodStatus = Arrays.asList(OrderStatusEnum.NO_PAY.getStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus());
    @Override
    public void cancle(OrdersCancelReqDTO ordersCancelReqDTO) {
        //1.判断订单是否存在
        Orders orders = this.getById(ordersCancelReqDTO.getId());
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }
        //2.判断订单状态合法性
        if (ObjectUtils.notEqual(orders.getOrderStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
            throw new ForbiddenOperationException("请求参数无效,非法状态");
        }
        //3.预约人数-1
        reservationSettingService.substract(orders.getReservationDate());

        owner.cancelForNoPay(orders.getId(), ordersCancelReqDTO.getCancelReason(), true);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancelForNoPay(Long orderId, String cancelReason, Boolean isPeople) {
        boolean update = lambdaUpdate().eq(Orders::getId, orderId)
                .set(Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus())//取消状态
                .update();
        if (!update) {
            throw new DBException("更新订单失败");
        }
        //向订单取消表中添加数据
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(orderId);//订单Id
        if (isPeople) {
            ordersCancelled.setCancellerId(UserThreadLocal.currentUserId());//取消订单人Id
            ordersCancelled.setCancellerName(UserThreadLocal.currentUser().getName());//取消订单人Name
            ordersCancelled.setCancellerType(UserThreadLocal.currentUser().getUserType());//取消订单人类型
        }
        ordersCancelled.setCancelReason(cancelReason);//取消订单原因
        ordersCancelled.setCancelTime(LocalDateTime.now());//取消时间

        //插入数据
        int insert = ordersCancelledMapper.insert(ordersCancelled);
        if (insert == 0) {
            throw new DBException("插入数据失败");
        }
    }

    /**
     * 订单退款
     *
     * @param ordersCancelReqDTO
     */
    @Autowired
    private OrdersRefundMapper ordersRefundMapper;


    @Override
    public void refund(OrdersCancelReqDTO ordersCancelReqDTO) {
        //1.判断订单是否存在
        Orders orders = this.getById(ordersCancelReqDTO.getId());
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }
        //2.判断订单状态合法性
        if (ObjectUtils.notEqual(orders.getOrderStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus())) {
            throw new ForbiddenOperationException("请求参数无效,非法状态");
        }
        //向订单取消表中添加数据
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(orders.getId());//订单Id

        ordersCancelled.setCancellerId(UserThreadLocal.currentUserId());//取消订单人Id
        ordersCancelled.setCancellerName(UserThreadLocal.currentUser().getName());//取消订单人Name
        ordersCancelled.setCancellerType(UserThreadLocal.currentUser().getUserType());//取消订单人类型
        ordersCancelled.setCancelReason(ordersCancelReqDTO.getCancelReason());//取消订单原因
        ordersCancelled.setCancelTime(LocalDateTime.now());//取消时间

        //插入数据
        int insert = ordersCancelledMapper.insert(ordersCancelled);
        if (insert == 0) {
            throw new DBException("插入数据失败");
        }
        //3.添加待退款记录表信息
        OrdersRefund ordersRefund = new OrdersRefund();
        ordersRefund.setId(orders.getId());//订单id
        ordersRefund.setTradingOrderNo(orders.getTradingOrderNo());//支付服务交易单号
        ordersRefund.setRealPayAmount(BigDecimal.valueOf(0.01));//实际金额

        int insert1 = ordersRefundMapper.insert(ordersRefund);
        if (insert1 == 0) {
            throw new DBException("添加待退款记录失败");
        }

        //4.更新订单表的退款相关字段的值
        boolean resultForOrder = owner.lambdaUpdate()
                .eq(Orders::getId, ordersRefund.getId())//订单id
                .set(Orders::getOrderStatus, OrderStatusEnum.CLOSED.getStatus())//设置已关闭
                .set(Orders::getPayStatus, OrderPayStatusEnum.REFUNDING.getStatus()) //设置支付状态为退款中
                .update();
        if (!resultForOrder) {
            throw new DBException("[退款操作]更新订单表失败，待退款记录信息：" + ordersRefund);
        }
    }


    /**
     * 管理端分页查询订单
     */
    @Override
    public PageResult<OrdersResDTO> pageQuery(OrdersPageQueryReqDTO ordersPageQueryReqDTO) {
        Page<Orders> page = new Page<>(ordersPageQueryReqDTO.getPageNo(), ordersPageQueryReqDTO.getPageSize());
        Page<Orders> ordersPage = lambdaQuery().eq(StringUtils.isNotBlank(ordersPageQueryReqDTO.getMemberPhone()), Orders::getMemberPhone, ordersPageQueryReqDTO.getMemberPhone())
                .eq(ObjectUtils.isNotNull(ordersPageQueryReqDTO.getOrderStatus()), Orders::getOrderStatus, ordersPageQueryReqDTO.getOrderStatus())
                .orderBy(ObjectUtils.isNotNull(ordersPageQueryReqDTO.getIsAsc1()), ordersPageQueryReqDTO.getIsAsc1(), Orders::getSortBy)
                //.orderBy(ObjectUtils.isNotNull(ordersPageQueryReqDTO.getIsAsc2()), ordersPageQueryReqDTO.getIsAsc2(), Orders::getSortBy)
                .page(page);
        List<Orders> ordersList = ordersPage.getRecords();
        List<OrdersResDTO> ordersResDTOS = BeanUtil.copyToList(ordersList, OrdersResDTO.class);
        return new PageResult<>(ordersPage.getPages(), ordersPage.getTotal(), ordersResDTOS);
    }


    /**
     * 根据状态统计数量
     */
    @Override
    public OrdersCountResDTO countByStatus() {
        OrdersCountResDTO ordersCountResDTO = new OrdersCountResDTO();
        //1.查询已体检数量
        ordersCountResDTO.setCompletedCheckupCount(countByStatus(OrderStatusEnum.COMPLETED_CHECKUP.getStatus()));
        //2.查询待体检数量
        ordersCountResDTO.setWaitingCheckupCount(countByStatus(OrderStatusEnum.WAITING_CHECKUP.getStatus()));
        //3.全部数量
        ordersCountResDTO.setTotalCount(count());
        //4.以取消数量
        ordersCountResDTO.setCancelledCount(countByStatus(OrderStatusEnum.CANCELLED.getStatus()));
        //5.待支付数量
        ordersCountResDTO.setNoPayCount(countByStatus(OrderStatusEnum.NO_PAY.getStatus()));
        //6.已关闭数量
        ordersCountResDTO.setClosedCount(countByStatus(OrderStatusEnum.CLOSED.getStatus()));
        return ordersCountResDTO;
    }

    private Integer countByStatus(Integer status) {
        return lambdaQuery().eq(Orders::getOrderStatus, status).count();
    }


    /**
     * 根据订单id查询
     */
    @Override
    public AdminOrdersDetailResDTO aggregation(Long id) {
        Orders orders = lambdaQuery().eq(Orders::getId, id)
                .one();
        if (orders == null) {
            throw new BadRequestException("请求参数无效，订单不存在");
        }
        AdminOrdersDetailResDTO.OrderInfo orderInfo = BeanUtil.toBean(orders, AdminOrdersDetailResDTO.OrderInfo.class);
        AdminOrdersDetailResDTO.PayInfo payInfo = BeanUtil.toBean(orders, AdminOrdersDetailResDTO.PayInfo.class);
        payInfo.setThirdOrderId(orders.getTransactionId());//第三方交易单号
        //除了未支付状态都设置为已支付
        if (ObjectUtils.equals(orders.getPayStatus(), OrderPayStatusEnum.NO_PAY.getStatus())) {
            payInfo.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        } else {
            payInfo.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        }
        AdminOrdersDetailResDTO.RefundInfo refundInfo = BeanUtil.toBean(orders, AdminOrdersDetailResDTO.RefundInfo.class);
        refundInfo.setRefundStatus(orders.getPayStatus());//退款状态
        AdminOrdersDetailResDTO.CancelInfo cancelInfo = BeanUtil.toBean(orders, AdminOrdersDetailResDTO.CancelInfo.class);

        OrdersCancelled ordersCancelled = ordersCancelledMapper.selectById(orders.getId());
        refundInfo.setCancelReason(ordersCancelled.getCancelReason());//退款原因
        refundInfo.setCancelTime(ordersCancelled.getCancelTime());//申请时间

        AdminOrdersDetailResDTO adminOrdersDetailResDTO = new AdminOrdersDetailResDTO();
        adminOrdersDetailResDTO.setOrderInfo(orderInfo);
        adminOrdersDetailResDTO.setPayInfo(payInfo);
        adminOrdersDetailResDTO.setRefundInfo(refundInfo);
        adminOrdersDetailResDTO.setCancelInfo(cancelInfo);
        return adminOrdersDetailResDTO;
    }

    /**
     * 滚动分页查询
     *
     * @param ordersStatus
     * @param sortBy       最后一个订单的sort_by字段值
     * @return
     */
    @Override
    public List<OrdersResDTO> userPageQuery(Integer ordersStatus, Long sortBy) {
        //设置默认页码和分页大小
        Integer pageSize = 10;
        Integer pageNum = 1;
        Page<Orders> ordersPage = new Page<>(pageNum, pageSize);
        //分页规则
        Page<Orders> page = lambdaQuery().eq(Orders::getMemberId, UserThreadLocal.currentUserId())
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrderStatus, ordersStatus)
                .orderByDesc(Orders::getSortBy)
                .gt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .page(ordersPage);

        List<OrdersResDTO> dtoList = page.getRecords().stream()
                .map(orders -> BeanUtil.toBean(orders, OrdersResDTO.class))
                .collect(Collectors.toList());
        return dtoList;
    }

    /**
     * 根据订单id查询
     *
     * @param id
     * @return
     */
    @Override
    public OrdersDetailResDTO detail(Long id) {
        Orders orders = getById(id);
        OrdersDetailResDTO ordersDetailResDTO = BeanUtils.copyBean(orders, OrdersDetailResDTO.class);
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }
        OrdersCancelled ordersCancelled = ordersCancelledMapper.selectById(id);
        if (ordersCancelled != null) {
            ordersDetailResDTO.setCancelReason(ordersCancelled.getCancelReason());
            ordersDetailResDTO.setCancelTime(ordersCancelled.getCancelTime());
        }
        return ordersDetailResDTO;
    }
}